/*
 * ntputil.c
 *
 *  Created on: 06.06.2012.
 *      Author: Aco
 */

#include "ntputil.h"
#include "../portutil/timing.h"

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <string.h>

// Kreira jedan klijentski NTP paket
NTPpack * ntp_createPacket()
{
	NTPpack *p = (NTPpack*)calloc(1, sizeof(NTPpack));
	p->VN = MY_NTP_VERSION;
	p->mode = NM_CLIENT;
	return p;
}

// Endianness flip 32
Uint32 ntp_reverseBytes32(Uint32 src)
{
	Uint32 dst = 0;
	unsigned char * sPtr = (unsigned char *)&src;
	unsigned char * dPtr = (unsigned char *)&dst;
	dPtr[0] = sPtr[3];
	dPtr[1] = sPtr[2];
	dPtr[2] = sPtr[1];
	dPtr[3] = sPtr[0];
	return dst;
}

// Endianness flip 16
Uint16 ntp_reverseBytes16(Uint16 src)
{
	return ((src & 0xFF) << 8) | (src >> 8);
}

// Zapis trenutnog sistemskog vremena u NTP obliku (sekunde:32 + fraction:32)
NTPtime64 ntp_getCurrentTime()
{
	Uint32 currentTime;
	NTPtime64 result;
	CUSecTime secTime;
	struct tm * timeinfo;

	// Sistemsko vrijeme iz standardne C biblioteke
	time((time_t *)&currentTime);
	timeinfo = localtime((time_t *)&currentTime);

	// Sekunde se oduzimaju i dodaju se preciznije sekunde i
	// mikrosekunde dobijene kroz API OS-a
	currentTime -= timeinfo->tm_sec;
	secTime = cu_getSecTime();
	currentTime += secTime.seconds;
	currentTime += 2208988800u;

	// Konverzija u big-endian fixed-point zapis po NTP specifikaciji
	result.seconds = ntp_reverseBytes32(currentTime);
	result.fraction = (Uint32)((double)(secTime.microSeconds / 1000) / 1000.0 * 4294967296.0);
	result.fraction = ntp_reverseBytes32(result.fraction);
	return result;
}

// Ljudski-citljiv zapis NTP timestampa
void ntp_formatTime64(NTPtime64 nt, char * buffer)
{
	struct tm * timeinfo;
	char tmp[8];
	if(nt.seconds == 0)
	{
		sprintf(buffer, "n/a");
		return;
	}
	nt.fraction = ntp_reverseBytes32(nt.fraction);
	nt.seconds = ntp_reverseBytes32(nt.seconds);
	nt.seconds -= 2208988800u;	// Broj sekundi od 1900. do 1970. godine,
								// konverzija u Unix vrijeme
	timeinfo = localtime ((time_t*)&(nt.seconds));
	strftime (buffer, 80, "%a %d.%m.%Y. %H:%M:%S", timeinfo);
	sprintf(tmp, ".%03.0f", (double)nt.fraction / 4294967.296);
	strcat(buffer, tmp);
}

// Milisekunde od fraction dijela NPT timestampa
int ntp_getMiliseconds(NTPtime64 * nt)
{
	return (int)((double)ntp_reverseBytes32(nt->fraction) / 4294967.296);
}

void ntp_printTime64(NTPtime64 nt)
{
	char buffer[64];
	ntp_formatTime64(nt, buffer);
	printf("%s", buffer);
}

// Formatiranje kraceg 32-bitnog zapisa, samo sekune
void ntp_formatTime32(NTPtime32 nt, char * buffer)
{
	sprintf(buffer, "%.5f sec", ntp_reverseBytes16(nt.seconds) +
			(ntp_reverseBytes16(nt.fraction) / 65536.0f));
}

void ntp_printTime32(NTPtime32 nt)
{
	char buffer[64];
	ntp_formatTime32(nt, buffer);
	printf("%s", buffer);
}

// Ispis svih podataka iz jednog NTP paketa, sve prema RFC specifikaciji
void ntp_packetInfo(NTPpack *np)
{
	if(np == NULL)
	{
		printf("NULL packet\n");
		return;
	}

	printf("LI = %d\n", np->LI);
	printf("Version = %d\n", np->VN);

	switch(np->mode)
	{
	case NM_BROADCAST: printf("Mode = Broadcast\n"); break;
	case NM_CLIENT: printf("Mode = Client\n"); break;
	case NM_CONTROL_MESSAGE: printf("Mode = Control message\n"); break;
	case NM_PRIVATE: printf("Mode = Reserved for private use\n"); break;
	case NM_RESERVED: printf("Mode = Reserved\n"); break;
	case NM_SERVER: printf("Mode = Server\n"); break;
	case NM_SYMMETRIC_ACTIVE: printf("Mode = Symmetric (active)\n"); break;
	case NM_SYMMETRIC_PASSIVE: printf("Mode = Symetric (passive)\n"); break;
	default: printf("Mode = INVALID (%d)\n", np->mode); break;
	}

	if(np->stratum == 0)
		printf("Stratum = Unspecified/invalid\n");
	else if(np->stratum == 1)
		printf("Stratum = Primary server\n");
	else if(np->stratum >= 2 && np->stratum <= 15)
		printf("Stratum = Secondary master (%d)\n", np->stratum);
	else if(np->stratum == 16)
		printf("Stratum = Unsynchronized\n");
	else
		printf("Stratum = Reserved (%d)\n", np->stratum);

	if(np->stratum <= 1)
	{
		printf("Identifier (txt) = %c%c%c%c\n",
				np->clockIdent.name[0],
				np->clockIdent.name[1],
				np->clockIdent.name[2],
				np->clockIdent.name[3]);
	}
	else
	{
		printf("Identifier (IP) = %d.%d.%d.%d\n",
				np->clockIdent.ipParts[0],
				np->clockIdent.ipParts[1],
				np->clockIdent.ipParts[2],
				np->clockIdent.ipParts[3]);
	}

	printf("Poll = %.5f sec\n", np->poll * log(2.0f));
	printf("Precision = %.5f sec\n", pow(2.0f, np->precision));
	printf("Root delay      : "); ntp_printTime32(np->rootDelay); puts("");
	printf("Root dispersion : "); ntp_printTime32(np->rootDispersion); puts("");


	printf("Reference time : "); ntp_printTime64(np->refTimestamp); puts("");
	printf("Originate time : "); ntp_printTime64(np->origTimestamp); puts("");
	printf("Receive time   : "); ntp_printTime64(np->recvTimestamp); puts("");
	printf("Transmit time  : "); ntp_printTime64(np->transmitTimestamp); puts("");

	printf("\n");
}
